package org.web.demo.servlet;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

/**
 * 在Servlet3.0以及之后的版本，支持使用 @WebServlet 注解式开发来代替配置 web.xml。
 * <p>
 * 请求转发介绍:
 * 1、请求转发是一种服务器的行为，其对浏览器而言是屏蔽的(记住请求转发与浏览器没有关系)。
 * 2、浏览器的地址栏不会发生变化
 * 3、请求的参数可以由源组件传递到目标组件(比如从当前Servlet添加新的参数传递给新的Servlet)
 * 4、请求对象和响应对象没有重新创建，而是传递给了目标组件
 * 5、请求转发可以帮助我们完成页面的跳转
 * 6、请求转发可以转发至WEB-INF里
 * 7、请求转发只能转发给当前项目的内部资源，不能转发至外部资源
 * <p>
 *
 * @author yexi
 */
@WebServlet(
        //Servlet的访问URL，支持多个。等价于value属性，但不能同时使用。
        urlPatterns = {"/dispatcher"},
        name = "请求转发测试Servlet",
        //Servlet的init参数。等同于 web.xml 中的 <init-param> 标签
        initParams = {
                @WebInitParam(name = "flag", value = "ture"),
                @WebInitParam(name = "flag2", value = "ture")
        },
        //声明Servlet是否支持异步操作模式。默认为false。
        asyncSupported = false,
        //设置是否自启动Servlet。默认为-1。等同于 web.xml 中的 <load-on-startup> 标签
        loadOnStartup = -1,
        smallIcon = "",
        largeIcon = "",
        description = "Servlet的描述",
        displayName = "Servlet的显示名称"
)
@Slf4j
public class DispatcherServlet extends HttpServlet {

    public DispatcherServlet() {
        log.info("DispatcherServlet实例化");
    }

    @Override
    public void init() throws ServletException {
        log.info("DispatcherServlet属性初始化");
    }

    @Override
    public void destroy() {
        log.info("DispatcherServlet对象销毁");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 接受请求参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //将信息放到 request 作用域中，请求转发的页面可以通过该名称获取到对应的值
        req.setAttribute("message","<br/><input type=\"button\" value=\"返回上一页（该按钮由req.setAttribute提供）\" onclick=\"history.back()\">");
        // 调用业务方法来处理请求，这里模拟和数据库中账号信息的匹配
        try {
            if (!"admin".equals(username)) {
                throw new RuntimeException("账号不存在");
            }
            if (!"1".equals(password)) {
                throw new RuntimeException("密码错误");
            }

            // 获得一个请求转发器
            RequestDispatcher requestDispatcher = req.getRequestDispatcher("/forward");
            resp.getWriter().println("使用forward转发之前所做的操作是无效的");
            // 调用forward或者include方法后，由请求转发器作出转发动作。(请求完全交给目标资源去处理,之后我们在源组件中作出的所有响应处理均无效)
            /*
                forward转发处理流程:
                1、清空Response存放响应正文数据的缓冲区。因此源Servlet生成的响应结果不会被发送到客户端，只有目标资源生成的响应结果才会被发送到客户端。
                   TIPS:如果源Servlet在进行请求转发之前，已经提交了响应(比如flushBuffer()、close()方法)，
                        那么forward方法会抛出IllegalStateException。为了避免该异常，不应该在源Servlet中提交响应结果。
                2、如果目标资源为Servlet或JSP，就调用它们的service()方法，把该方法产生的响应结果发送到客户端；
                  如果目标资源为文件系统中的静态HTML文档，就读取文档中的数据把它发送到客户端。
            */
            requestDispatcher.forward(req, resp);
            resp.getWriter().println("使用forward转发后续的操作也是无效的");
        } catch (Exception e) {
            RequestDispatcher requestDispatcher = req.getRequestDispatcher("/include");
            /*
                include转发处理流程:
                1、如果目标资源为Servlet或JSP，就调用他们的相应的service()方法，把该方法产生的响应正文添加到源Servlet的响应结果中；
                如果目标组件为HTML文档，就直接把文档的内容添加到源Servlet的响应结果中。
                2、返回到源Servlet的服务方法中，继续执行后续代码块。
             */
            requestDispatcher.include(req, resp);
            resp.getWriter().println("<br/><h2>使用include转发可追加后续内容</h2>");
        }
    }
}
